package com.shao.utils;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

/**
 * @author PaoFu
 * @version 1.0
 * @data 2025/5/15 11:15
 */
@Slf4j
public class PackageUtils {

    /**
     * 基础扫包
     * @param basePackage 包名
     * @return Set<Class<?>>
     */
    public static Set<Class<?>> scanPackage(String basePackage) throws IOException {
        Set<Class<?>> classSet = new HashSet<>();
        String path = basePackage.replaceAll("\\.", "/");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> resources = classLoader.getResources(path);
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            if ("file".equalsIgnoreCase(resource.getProtocol())) {
                // 处理文件目录
                File dir = new File(resource.getFile());
                scanDirectory(dir, basePackage, classSet);
            } else if ("jar".equalsIgnoreCase(resource.getProtocol())) {
                // 处理jar文件
                JarURLConnection jarURLConn = (JarURLConnection) resource.openConnection();
                scanJar(jarURLConn.getJarFile(), basePackage, classSet);
            } else {
                log.error("没有兼容此【{}】后缀的扫包模块", resource.getProtocol());
            }
        }
        return classSet;
    }

    public static Set<Class<?>> scanAnnotatedClasses(String basePackage, Class<? extends Annotation> annotation) throws IOException {
        Set<Class<?>> classes = scanPackage(basePackage);
        return classes.stream()
                .filter(clazz -> clazz.isAnnotationPresent(annotation))
                .collect(Collectors.toSet());
    }

    /**
     * 扫描目录
     * @param dir 目录文件
     * @param basePackage 基础路径
     * @param classSet class文件集
     */
    private static void scanDirectory(File dir, String basePackage, Set<Class<?>> classSet) {
        File[] files = dir.listFiles();
        if (files == null) return;
        for (File file : files) {
            if (file.isDirectory()) {
                // 递归扫包
                scanDirectory(file, basePackage + "." + file.getName(), classSet);
            } else if (file.getName().endsWith(".class")) {
                // 转换为类名
                String classname = basePackage + "." + file.getName().replace(".class", "");
                loadClass(classname, classSet);
            }
        }
    }

    /**
     * 加载类名
     * @param classname 类名
     * @param classSet 类集
     */
    private static void loadClass(String classname, Set<Class<?>> classSet) {
        try {
            Class<?> aClass = Class.forName(classname);
            classSet.add(aClass);
        } catch (ClassNotFoundException e) {
            log.error("加载【{}】此类时发生异常", classname);
        }
    }

    /**
     * 扫描jar包
     * @param jarFile jar文件
     * @param basePackage 基础包路径
     * @param classSet class文件集
     */
    private static void scanJar(JarFile jarFile, String basePackage, Set<Class<?>> classSet) {
        String path = basePackage.replace(".", "/");
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry jarEntry = entries.nextElement();
            String name = jarEntry.getName();
            if (name.startsWith(path) && name.endsWith(".class") && !name.contains("&")) {
                String classname = name.replace(".", "/").replace(".class", "");
                loadClass(classname, classSet);
            }
        }
    }
}
